/* sub_header.cpp */

#include "sub_header.h"
#include <boost/cstdint.hpp>
#include <boost/algorithm/string/trim.hpp>

#ifndef NO_NEED_TO_SWAP_ENDIAN
#include <dodo_utils/swap_endian.h>
#endif // NO_NEED_TO_SWAP_ENDIAN

namespace dodomet
{
namespace mm5
{
// Sub_Header_Impl
Sub_Header_Impl::Sub_Header_Impl()
	:
	use_count_(1)
{
}

Sub_Header_Impl::Sub_Header_Impl(const Sub_Header_Impl& orig)
	:
	ndim_(orig.ndim_),
	xtime_(orig.xtime_),
	staggering_(orig.staggering_),
	ordering_(orig.ordering_),
	field_pos_(orig.field_pos_),
	start_time_(orig.start_time_),
	use_count_(1)
{
	for(int i = 0; i < 4; ++i)
	{
		start_index_[i] = orig.start_index_[i];
		end_index_[i] = orig.end_index_[i];
	}
	for(int i = 0; i < 24 + 1; ++i)
		current_date_[i] = orig.current_date_[i];
	for(int i = 0; i < 9 + 1; ++i)
		name_[i] = orig.name_[i];
	for(int i = 0; i < 25 + 1; ++i)
		unit_[i] = orig.unit_[i];
	for(int i = 0; i < 46 + 1; ++i)
		description_[i] = orig.description_[i];
}
// reading from stream
void Sub_Header_Impl::read(std::fstream& stream)
{
	uint32_t dum;
	stream.read(reinterpret_cast<char*>(&dum), sizeof(uint32_t));
	
	stream.read(reinterpret_cast<char*>(&ndim_), sizeof(int_type));
#ifndef NO_NEED_TO_SWAP_ENDIAN
	dodomet::swap_endian(ndim_);
#endif // NO_NEED_TO_SWAP_ENDIAN
	stream.read(reinterpret_cast<char*>(&start_index_[0]), sizeof(int_type) * 4);
#ifndef NO_NEED_TO_SWAP_ENDIAN
	for(int i = 0; i < 4; ++i)
	{
		dodomet::swap_endian(start_index_[i]);
	}
#endif // NO_NEED_TO_SWAP_ENDIAN
	stream.read(reinterpret_cast<char*>(&end_index_[0]), sizeof(int_type) * 4);
#ifndef NO_NEED_TO_SWAP_ENDIAN
	for(int i = 0; i < 4; ++i)
	{
		dodomet::swap_endian(end_index_[i]);
	}
#endif // NO_NEED_TO_SWAP_ENDIAN

	stream.read(reinterpret_cast<char*>(&xtime_), sizeof(real_type));
#ifndef NO_NEED_TO_SWAP_ENDIAN
	dodomet::swap_endian(xtime_);
#endif // NO_NEED_TO_SWAP_ENDIAN

	char tmp[5];
	stream.read(tmp, 4);
	staggering_ = char_to_staggering(tmp[0]);

	stream.read(tmp, 4);
	tmp[4] = '\0';
	std::string tmpstr(tmp);
	boost::trim(tmpstr);

	ordering_ = string_to_ordering(tmpstr);

	stream.read(current_date_, 24);
	current_date_[24] = '\0';
	stream.read(name_, 9);
	name_[9] = '\0';
	stream.read(unit_, 25);
	unit_[25] = '\0';
	stream.read(description_, 46);
	description_[46] = '\0';
	stream.read(reinterpret_cast<char*>(&dum), sizeof(dum));

	field_pos_ = stream.tellg();
}

void Sub_Header_Impl::set_start_time(boost::posix_time::ptime start_time)
{
	start_time_ = start_time;
}

boost::posix_time::ptime Sub_Header_Impl::start_time() const
{
	return start_time_;
}

int Sub_Header_Impl::ndim() const
{
	return ndim_;
}

int Sub_Header_Impl::start_index(int dim) const
{
	return start_index_[dim];
}

int Sub_Header_Impl::end_index(int dim) const
{
	return end_index_[dim];
}

std::string Sub_Header_Impl::name() const
{
	std::string name_str(name_);
	boost::trim(name_str);
	return name_str;
}

std::string Sub_Header_Impl::unit() const
{
	std::string unit_str(unit_);
	boost::trim(unit_str);
	return unit_str;
}

std::string Sub_Header_Impl::description() const
{
	std::string desc_str(description_);
	boost::trim(desc_str);
	return desc_str;
}

boost::posix_time::time_duration Sub_Header_Impl::xtime() const
{
	return boost::posix_time::seconds(static_cast<long>(xtime_ * 60));
}

Sub_Header_Impl::real_type Sub_Header_Impl::xtime_real() const
{
	return xtime_;
}

int Sub_Header_Impl::staggering() const
{
	return staggering_;
}

int Sub_Header_Impl::ordering() const
{
	return ordering_;
}

std::streampos Sub_Header_Impl::field_pos() const
{
	return field_pos_;
}
// Sub_Header
Sub_Header::Sub_Header()
	: impl_(new Sub_Header_Impl())
{
}

Sub_Header::Sub_Header(const Sub_Header& orig)
	: impl_(orig.impl_)
{
	++impl_->use_count_;
}

Sub_Header::~Sub_Header()
{
	if(--impl_->use_count_ == 0)
		delete impl_;
}

Sub_Header& Sub_Header::operator= (const Sub_Header& orig)
{
	++orig.impl_->use_count_;
	if(--impl_->use_count_ == 0)
		delete impl_;
	impl_ = orig.impl_;
}

void Sub_Header::on_write()
{
	if(impl_->use_count_ == 1)
		return;
	Sub_Header_Impl *tmp = impl_;
	impl_ = new Sub_Header_Impl(*tmp);
	--tmp->use_count_;
}

void Sub_Header::read(std::fstream& stream)
{
	on_write();
	impl_->read(stream);
}

void Sub_Header::set_start_time(boost::posix_time::ptime start_time)
{
	on_write();
	impl_->set_start_time(start_time);
}

boost::posix_time::ptime Sub_Header::start_time() const
{
	return impl_->start_time();
}

int Sub_Header::ndim() const
{
	return impl_->ndim();
}

int Sub_Header::start_index(int dim) const
{
	return impl_->start_index(dim);
}

int Sub_Header::end_index(int dim) const
{
	return impl_->end_index(dim);
}

std::string Sub_Header::name() const
{
	return impl_->name();
}

std::string Sub_Header::unit() const
{
	return impl_->unit();
}

std::string Sub_Header::description() const
{
	return impl_->description();
}

boost::posix_time::time_duration Sub_Header::xtime() const
{
	return impl_->xtime();
}

Sub_Header::real_type Sub_Header::xtime_real() const
{
	return impl_->xtime_real();
}

int Sub_Header::staggering() const
{
	return impl_->staggering();
}

int Sub_Header::ordering() const
{
	return impl_->ordering();
}

std::streampos Sub_Header::field_pos() const
{
	return impl_->field_pos();
}
} // namespace mm5
} // namespace dodomet
